﻿#include "GeomTurboDataExchange.h"
#include <TColgp_Array1OfPnt.hxx>
#include <GeomAPI_PointsToBSpline.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRep_Builder.hxx>
#include <Geom_BSplineCurve.hxx>
#include <gp_Ax1.hxx>
#include <BRepPrimAPI_MakeRevol.hxx>
#include <BRepOffsetAPI_ThruSections.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <GeomAPI_Interpolate.hxx>
#include <gp_Trsf.hxx>
#include <TopoDS_Compound.hxx>
#include <QFileInfo>
#include <QDir>
#include <QFile>
#include <QCoreApplication>
#include "Geometry/geometrySet.h"
#include "Geometry/geometryData.h"
#include <math.h>
#include "Common/C3fdProject.h"

namespace Geometry
{
	GeomTurboDataExchange::GeomTurboDataExchange(const QString &_fileName, GeometryOperation operation, GUI::MainWindow *mw, QList<GeometrySet *> &res, int KernalId)
		: GeometryThreadBase(mw, res), _fileName(_fileName), _operation(operation), _mainWindow(mw)
	{
		Q_UNUSED(KernalId);
	}

	void GeomTurboDataExchange::run()
	{
		ModuleBase::ThreadTask::run();
		switch (_operation)
		{
		case GEOMETRY_READ:
			emit showInformation(tr("Import Geometry File From \"%1\"").arg(_fileName));
			_isRead = readGeomTurbo();
			break;
		case GEOMETRY_WRITE:
			emit showInformation(tr("Export Geometry File To \"%1\"").arg(_fileName));
			_isWrite = writeGeomTurbo();
			break;
		}
		deriveGeometryTaskFinished();
	}

	void GeomTurboDataExchange::setFirstThreadFlag(bool flag)
	{
		m_FirstThreadFlag = flag;
	}

	bool GeomTurboDataExchange::readGeomTurbo()
	{
		if (_fileName.isEmpty())
		{
			m_ErrorString = QObject::tr("File name is empty!");
			return false;
		}
		QFile infile(_fileName);
		if (!infile.exists())
		{
			m_ErrorString = QObject::tr("File is not exists!");
			return false;
		}

		if (!infile.open(QIODevice::ReadOnly))
		{
			m_ErrorString = QObject::tr("Failed to open file!");
			return false;
		}

		QString line = "";
		bool result = true;

		while (result && !infile.atEnd())
		{
			line = QString(infile.readLine()).simplified().toLower();
			if (line.startsWith("geometric_tolerance"))
			{
				bool ok;
				double num = line.replace("geometric_tolerance", "").simplified().toDouble(&ok);
				if (ok)
				{
					m_Tolerance = num;
				}
			}
			else if (line.startsWith("number_of_blades"))
			{
				bool ok;
				int num = line.replace("number_of_blades", "").simplified().toInt(&ok);
				if (ok)
				{
					m_NumOfBlades = num;
				}
			}
			else if (line.startsWith("hub"))
			{
				result = parseHubData(infile);
			}
			else if (line.startsWith("shroud"))
			{
				result = parseShroudData(infile);
			}
			else if (line.startsWith("suction"))
			{
				result = parseSuctionData(infile);
			}
			else if (line.startsWith("pressure"))
			{
				result = parsePressureData(infile);
			}
		}

		if (!result)
			return false;

		int numOfSection = m_Suction.size();
		if (numOfSection != m_Pressure.size())
		{
			return false;
		}

		QFileInfo info(_fileName);
		const QString name = info.completeBaseName();

		// 判断是否已经加载hub和shroud
		bool notHasHubAndShroud = true;
		if (m_FirstThreadFlag)
		{
			auto geometryData = GeometryData::getInstance();
			int existsGeomtrySetNum = geometryData->getGeometrySetCount();
			QString tempName = "";
			for (int i = 0; i < existsGeomtrySetNum; ++i)
			{
				tempName = geometryData->getGeometrySetAt(i)->getName();
				if (tempName == "Hub" || tempName == "Shroud")
				{
					notHasHubAndShroud = false;
					break;
				}
			}
		}
		else
		{
			notHasHubAndShroud = false;
		}

		gp_Ax1 zAxis = gp::OZ();

		if (notHasHubAndShroud)
		{
			// 叶根hub
			TopoDS_Shape hubEdge = createSpline(m_Hub);
			TopoDS_Shape *hubShape = new TopoDS_Shape;
			*hubShape = BRepPrimAPI_MakeRevol(hubEdge, zAxis, Standard_False);

			auto hubGeoSet = new GeometrySet(NONE);
			hubGeoSet->setShape(hubShape);
			hubGeoSet->setName("Hub");
			GeometryData::getInstance()->appendGeometrySet(hubGeoSet);
			getResult().append(hubGeoSet);

			// 叶顶shroud
			TopoDS_Shape shroudEdge = createSpline(m_Shroud);
			TopoDS_Shape *shroudShape = new TopoDS_Shape;
			*shroudShape = BRepPrimAPI_MakeRevol(shroudEdge, zAxis, Standard_False);
			auto shroudGeoSet = new GeometrySet(NONE);
			shroudGeoSet->setShape(shroudShape);
			shroudGeoSet->setName("Shroud");
			GeometryData::getInstance()->appendGeometrySet(shroudGeoSet);
			getResult().append(shroudGeoSet);
		}

		// 叶片
		BRepOffsetAPI_ThruSections bladeSections = new BRepOffsetAPI_ThruSections(true, false, m_Tolerance);
		for (int i = 0; i < numOfSection; ++i)
		{
			TopoDS_Edge suctionEdge = createSpline(m_Suction.at(i));
			TopoDS_Edge pressureEdge = createSpline(m_Pressure.at(i));
			TopoDS_Wire bladeSectionWire = BRepBuilderAPI_MakeWire(suctionEdge, pressureEdge);
			bladeSections.AddWire(bladeSectionWire);
		}
		bladeSections.Build();

		if (bladeSections.IsDone())
		{
			TopoDS_Shape *bladeShape = new TopoDS_Shape;
			*bladeShape = bladeSections.Shape();
			auto bladeGeoSet = new GeometrySet(NONE);
			bladeGeoSet->setShape(bladeShape);
			bladeGeoSet->setName(QString("%1-Blade").arg(name));
			GeometryData::getInstance()->appendGeometrySet(bladeGeoSet);
			getResult().append(bladeGeoSet);
		}

		if (m_NumOfBlades > 0)
		{
			double angleBetweenTwoBlade = 2 * M_PI / m_NumOfBlades;
			gp_Trsf aTrsf;
			TopoDS_Compound aRes;
			BRep_Builder aBuilder;
			aBuilder.MakeCompound(aRes);
			for (int i = 1; i < m_NumOfBlades; ++i)
			{
				double angle = angleBetweenTwoBlade * i;
				aTrsf.SetRotation(zAxis, angle);
				BRepBuilderAPI_Transform aBRespTrsf(bladeSections.Shape(), aTrsf, true);
				aBRespTrsf.Build();
				if (aBRespTrsf.IsDone())
				{
					aBuilder.Add(aRes, aBRespTrsf.Shape());
				}
			}

			TopoDS_Shape *duplicatedBladeShape = new TopoDS_Shape;
			*duplicatedBladeShape = aRes;
			auto duplicatedBladeGeoSet = new GeometrySet(NONE);
			duplicatedBladeGeoSet->setShape(duplicatedBladeShape);
			duplicatedBladeGeoSet->setName(QString("%1-DuplicatedBlades-%2").arg(name).arg(m_NumOfBlades));
			GeometryData::getInstance()->appendGeometrySet(duplicatedBladeGeoSet);
			getResult().append(duplicatedBladeGeoSet);
		}

		auto* currentCase = Common::C3fdProject::instance()->getCurrentCase();
		if (currentCase != nullptr) {
            auto geom = new Common::GeomTurbo;
            geom->setNumOfBlades(m_NumOfBlades);
			currentCase->addGeomTurboFile(geom);
		}
		return true;
	}

	bool GeomTurboDataExchange::writeGeomTurbo()
	{
		return false;
	}

	void GeomTurboDataExchange::deriveGeometryTaskFinished()
	{
		ModuleBase::Message msg;
		if (_operation == GEOMETRY_READ)
		{
			if (_isRead)
			{
				emit updateActionsStates();
				emit showInformation(QString("Successful Import Geometry From \"%1\"").arg(_fileName));
				msg.type = Common::Message::Normal;
				msg.message = QString("Successful Import Geometry From \"%1\"").arg(_fileName);
				auto result = getResult();

				for (auto set : result)
				{
					if (set != result.last())
						emit ShowSetSig(set, false);
					else
						emit ShowSetSig(set);
				}

				QString casePath = Common::C3fdProject::instance()->getCurrentProjectDir();
				QString meshDirName = Common::C3fdProject::instance()->getMeshDirName();

				QFileInfo fileInfo(_fileName);
				QString destFileName = casePath + meshDirName + QDir::separator() + fileInfo.fileName();
				if (QFile::exists(destFileName)) {
					QFile::remove(destFileName);
				}
				if (QFile::copy(_fileName, destFileName))
				{
					emit updateMeshRowTreeSig();
					//auto*
				}
			}
			else
			{
				emit showInformation(QString("Failed Import Geometry From \"%1\"").arg(_fileName));
				msg.type = Common::Message::Error;
				msg.message = QString("Failed Import Geometry From \"%1\"").arg(_fileName);
			}
		}
		else if (_operation == GEOMETRY_WRITE)
		{
			if (_isWrite)
			{
				emit showInformation(QString("Successful Export Geometry From \"%1\"").arg(_fileName));
			}
			else
			{
				emit showInformation(QString("Failed Export Geometry From \"%1\"").arg(_fileName));
				msg.type = Common::Message::Error;
				msg.message = QString("Failed Export Geometry From \"%1\"").arg(_fileName);
			}
		}
		emit showGeometryMessageSig(msg);
		defaultGeometryFinished();
	}

	bool GeomTurboDataExchange::parseHubData(QFile &file)
	{
		int tempNum = 0;
		bool tempOk;
		bool result = true;
		double v1, v2;

		// ZR
		file.readLine();
		// num of points
		tempNum = file.readLine().simplified().toInt(&tempOk);
		if (tempOk)
		{
			for (int i = 0; i < tempNum; ++i)
			{
				auto values = file.readLine().simplified().split(' ');
				if (values.length() != 2)
				{
					m_ErrorString = QObject::tr("Error Format in hub data: Length!");
					result = false;
					break;
				}
				v1 = values.at(0).toDouble(&tempOk);
				if (!tempOk)
				{
					m_ErrorString = QObject::tr("Error Format in hub data: Z!");
					result = false;
					break;
				}
				v2 = values.at(1).toDouble(&tempOk);
				if (!tempOk)
				{
					m_ErrorString = QObject::tr("Error Format in hub data: R!");
					result = false;
					break;
				}
				m_Hub.push_back(zrToXyz(v1, v2));
			}
		}
		else
		{
			// 发生错误，退出程序
			m_ErrorString = QObject::tr("Error Format in hub data!");
			result = false;
		}
		return result;
	}

	bool GeomTurboDataExchange::parseShroudData(QFile &file)
	{
		int tempNum = 0;
		bool tempOk;
		bool result = true;
		double v1, v2;

		// ZR
		file.readLine();
		// num of points
		tempNum = file.readLine().simplified().toInt(&tempOk);
		if (tempOk)
		{
			for (int i = 0; i < tempNum; ++i)
			{
				auto values = file.readLine().simplified().split(' ');
				if (values.length() != 2)
				{
					m_ErrorString = QObject::tr("Error Format in shroud data: Length!");
					result = false;
					break;
				}
				v1 = values.at(0).toDouble(&tempOk);
				if (!tempOk)
				{
					m_ErrorString = QObject::tr("Error Format in shroud data: Z!");
					result = false;
					break;
				}
				v2 = values.at(1).toDouble(&tempOk);
				if (!tempOk)
				{
					m_ErrorString = QObject::tr("Error Format in shroud data: R!");
					result = false;
					break;
				}
				m_Shroud.push_back(zrToXyz(v1, v2));
			}
		}
		else
		{
			// 发生错误，退出程序
			m_ErrorString = QObject::tr("Error Format in shroud data!");
			result = false;
		}
		return result;
	}

	bool GeomTurboDataExchange::parseSuctionData(QFile &file)
	{
		bool tempOk, result = true;
		int tempNum = 0;
		QString line = "";
		QList<QList<double>> section;

		// SECTIONAL
		file.readLine();
		// num of section
		tempNum = file.readLine().simplified().toInt(&tempOk);
		if (tempOk)
		{
			while (tempNum > 0)
			{
				line = QString(file.readLine()).simplified().toLower();
				// if (line.startsWith("#section"))
				if (line.trimmed().toLower() == "xyz")
				{
					section = parseSectionData(file, &tempOk);
					if (tempOk)
					{
						m_Suction.push_back(section);
					}
					--tempNum;
				}
			}
		}
		else
		{
			// 发生错误，退出程序
			m_ErrorString = QObject::tr("Error Format in suction data!");
			result = false;
		}
		return result;
	}

	bool GeomTurboDataExchange::parsePressureData(QFile &file)
	{
		bool tempOk, result = true;
		int tempNum = 0;
		QString line = "";
		QList<QList<double>> section;

		// SECTIONAL
		file.readLine();
		// num of section
		tempNum = file.readLine().simplified().toInt(&tempOk);
		if (tempOk)
		{
			while (tempNum > 0)
			{
				line = QString(file.readLine()).simplified().toLower();
				// if (line.startsWith("#section"))
				if (line.trimmed().toLower() == "xyz")
				{
					section = parseSectionData(file, &tempOk);
					if (tempOk)
					{
						m_Pressure.push_back(section);
					}
					--tempNum;
				}
			}
		}
		else
		{
			// 发生错误，退出程序
			m_ErrorString = QObject::tr("Error Format in pressure data!");
			result = false;
		}
		return result;
	}

	// （Z=轴向，R=径向）
	// （X=径向，Y=周向，Z=轴向）
	QList<double> GeomTurboDataExchange::zrToXyz(double z, double r)
	{
		QList<double> p;
		p.push_back(r);
		p.push_back(0.0);
		p.push_back(z);
		return p;
	}

	QList<QList<double>> GeomTurboDataExchange::parseSectionData(QFile &file, bool *ok)
	{
		QList<QList<double>> section;
		int tempNum = 0;
		bool tempOk;
		bool result = true;
		double v1, v2, v3;

		// XYZ
		// file.readLine();
		// num of points
		tempNum = file.readLine().simplified().toInt(&tempOk);
		if (tempOk)
		{
			for (int i = 0; i < tempNum; ++i)
			{
				auto values = file.readLine().simplified().split(' ');
				if (values.length() != 3)
				{
					m_ErrorString = QObject::tr("Error Format in section data: Length!");
					result = false;
					break;
				}
				v1 = values.at(0).toDouble(&tempOk);
				if (!tempOk)
				{
					m_ErrorString = QObject::tr("Error Format in section data: X!");
					result = false;
					break;
				}
				v2 = values.at(1).toDouble(&tempOk);
				if (!tempOk)
				{
					m_ErrorString = QObject::tr("Error Format in section data: Y!");
					result = false;
					break;
				}
				v3 = values.at(2).toDouble(&tempOk);
				if (!tempOk)
				{
					m_ErrorString = QObject::tr("Error Format in section data: Z!");
					result = false;
					break;
				}
				QList<double> p;
				p.push_back(v1);
				p.push_back(v2);
				p.push_back(v3);

				section.push_back(p);
			}
		}
		else
		{
			// 发生错误，退出程序
			m_ErrorString = QObject::tr("Error Format in section data!");
			result = false;
		}
		if (ok != nullptr)
		{
			*ok = result;
		}
		return section;
	}

	// 该方法生成的B样条曲线有突变点
	TopoDS_Edge GeomTurboDataExchange::createSpline(QList<QList<double>> points, SplineType type)
	{
		Q_UNUSED(type);
		/// 均匀B样条，节点向量中的节点值成等差排布
		/// 均匀B样条的基函数呈周期性，即所有的基函数有相同的形状
		/// 每个后续基函数仅仅市前面基函数在新位置上的重复
		TopoDS_Edge anEdge;
		QVector<gp_Pnt> pointsArr;
		for (auto point : points)
		{
			if (point.size() == 3)
			{
				pointsArr.push_back(gp_Pnt(point.at(0), point.at(1), point.at(2)));
			}
			else
			{
				continue;
			}
		}
		int size = pointsArr.length();
		TColgp_Array1OfPnt array1(1, size);
		for (int i = 0; i < size; i++)
		{
			array1.SetValue(i + 1, pointsArr[i]);
		}
		GeomAPI_PointsToBSpline outline(array1);
		if (outline.IsDone())
		{
			Handle(Geom_BSplineCurve) hCurve = outline.Curve();
			anEdge = BRepBuilderAPI_MakeEdge(hCurve);
		}
		return anEdge;
	}

	/*TopoDS_Edge GeomTurboDataExchange::createSpline(QList<QList<double>> points, SplineType type)
	{
		Q_UNUSED(type);
		TopoDS_Edge anEdge;
		QVector<gp_Pnt> pointsArr;
		for (auto point : points) {
			if (point.size() == 3) {
				pointsArr.push_back(gp_Pnt(point.at(0), point.at(1), point.at(2)));
			}
			else {
				continue;
			}
		}
		int size = pointsArr.length();

		Handle(TColgp_HArray1OfPnt) pts = new TColgp_HArray1OfPnt(1, size);

		for (int i = 0; i < size; i++)
		{
			pts->SetValue(i + 1, pointsArr[i]);
		}
		GeomAPI_Interpolate interp(pts, Standard_False, 1e-6);
		gp_Vec v(0.0, 0.0, 0.0);
		TColgp_Array1OfVec vtan(v, 1, size);
		Handle(TColStd_HArray1OfBoolean) flags = new TColStd_HArray1OfBoolean(1, size, false);
		// define tangents and flags for every pole, any data is ok, not used.
		interp.Load(vtan, flags, Standard_False);
		interp.Perform();

		if (interp.IsDone())
		{
			Handle(Geom_BSplineCurve) hCurve = interp.Curve();
			anEdge = BRepBuilderAPI_MakeEdge(hCurve);

		}
		return anEdge;
	}*/
}
